import { createElement } from "./createElement"
import patch, { isSameVNode } from "./patch"
import patchVNode from "./patchVNode"

/**
 *
 * @param {Node} parentElm
 * @param {VNode} oldChildren
 * @param {VNode} newChildren
 */
export default function updateChildren(parentElm, oldChildren, newChildren) {
  let newStartIndex = 0  // 新前
  let oldStartIndex = 0  // 旧前
  let newEndIndex = newChildren.length - 1 // 新后
  let oldEndIndex = oldChildren.length - 1 // 旧后
  let keyMap = null;  // key : indexInOld
  // VNode
  let newStartVNode = newChildren[newStartIndex]
  let newEndVNode = newChildren[newEndIndex]
  let oldStartVNode = oldChildren[oldStartIndex]
  let oldEndVNode = oldChildren[oldEndIndex]

  while (oldStartIndex <= oldEndIndex && newStartIndex <= newEndIndex) {
    if (!oldStartVNode) {
      oldStartVNode = oldChildren[++oldStartIndex]
    } else if (!oldEndVNode) {
      oldEndVNode = oldChildren[--oldEndIndex]
    } else if (!newStartVNode) {
      newStartVNode = newChildren[++newStartIndex]
    } else if (!newEndVNode) {
      newEndVNode = newChildren[--newEndIndex]
    }
    // 新前和旧前
    else if (isSameVNode(oldStartVNode, newStartVNode)) {
      patchVNode(oldStartVNode, newStartVNode)
      newStartVNode = newChildren[++newStartIndex]
      oldStartVNode = oldChildren[++oldStartIndex]
    } else if (isSameVNode(oldEndVNode, newEndVNode)) {
      // 新后和旧后
      patch(oldEndVNode, newEndVNode)
      newEndVNode = newChildren[--newEndIndex]
      oldEndVNode = oldChildren[--oldEndIndex]
    } else if (isSameVNode(oldStartVNode, newEndVNode)) {
      /*
       新后与旧前, 要进oldStartVnode移动到oldEndVnode对应真实DOM后面
       这里解释下，因为先调用patchVNode将newEndVNode patch到了oldStartVNode，此时oldStartVNode就相当于
       createElement(newEndVNode)之后的newEndVNode，接下来就是要移动结点。
       新的最后结点和旧的最前结点是same, 则说明旧的结点(oldStartVNode)要移动要新的位置，那么就是oldEndVNode的后面
      */
      patchVNode(oldStartVNode, newEndVNode)
      parentElm.insertBefore(oldStartVNode.elm, oldEndVNode.elm.nextSibling)
      newEndVNode = newChildren[--newEndIndex]
      oldStartVNode = oldChildren[++oldStartIndex]
    } else if (isSameVNode(oldEndVNode, newStartVNode)) {
      // 新前与旧后， 要进oldEndVnode移动到oldStartVnode前面
      patchVNode(oldEndVNode, newStartVNode)
      parentElm.insertBefore(oldEndVNode.elm, oldStartVNode.elm)
      newStartVNode = newChildren[++newStartIndex]
      oldEndVNode = oldChildren[--oldEndIndex]
    } else {
      // 4中情况都未匹配的情况
      /*
      在oldChildren中，查找newStartVNode
      */
      if (!keyMap) {
        keyMap = {}
        for (let i = oldStartIndex; i <= oldEndIndex; i++) {
          if (oldChildren[i].key) {
            keyMap[oldChildren[i].key] = i
          }
        }
      }
      const indexInOld = keyMap[newStartVNode.key];
      if (indexInOld === undefined) {
        // 新的Vnode
        parentElm.insertBefore(createElement(newStartVNode), oldStartVNode.elm)
      } else {
        // 找到了, 则移动结点
        const toMoveVNode = oldChildren[indexInOld]
        patchVNode(toMoveVNode, newStartVNode)
        // 这里注意，因为要标记这个toMoveVNode已经处理过了，在下次循环判空跳过，故标记为undefined
        oldChildren[indexInOld] = undefined
        parentElm.insertBefore(toMoveVNode.elm, oldStartVNode.elm)
      }
      // 指针后移
      newStartVNode = newChildren[++newStartIndex]
    }
  }
  // 还剩有未处理结点
  // 新结点还有新增的结点, 全部插到旧结点的最后面
  if (newStartIndex <= newEndIndex) {
    const target = oldStartVNode ? oldStartVNode.elm : null;
    for (let i = newStartIndex; i <= newEndIndex; i++) {
      parentElm.insertBefore(createElement(newChildren[i]), target)
    }
  } else if (oldStartIndex <= oldEndIndex) {
    // 旧结点还有剩余的，说明新结点中有删除的
    // 将其全部删除
    for (let i = oldStartIndex; i <= oldEndIndex; i++) {
      if (oldChildren[i]) {
        parentElm.removeChild(oldChildren[i].elm)
      }
    }
  }
}